﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Model.LiveControl;

namespace Providers.LiveControl.Server
{
    public class WindowApi
    {
        private static Dictionary<int, Process> _dicProcesses;

        /// <summary>
        ///     键盘事件
        /// </summary>
        /// <param name="bVk">虚拟键值</param>
        /// <param name="bScan">定义该键的硬件扫描码，一般为0</param>
        /// <param name="dwFlags">这里是整数类型  0 为按下，2为释放</param>
        /// <param name="dwExtraInfo">这里是整数类型 一般情况下设成为 0</param>
        [DllImport("user32.dll", EntryPoint = "keybd_event")]
        public static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, uint dwExtraInfo);

        [DllImport("user32.dll")]
        public static extern int mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetForegroundWindow();

        public enum ShowWindowCmd : uint
        {
            /// <summary>
            /// 隐藏窗口并激活其他窗口
            /// </summary>
            SwHide = 0,

            /// <summary>
            /// 激活并显示一个窗口。如果窗口被最小化或最大化， 系统将其恢复到原来的尺寸和大小。
            /// 应用程序在第一次显示窗口的时候应该指定此标志。
            /// </summary>
            SwShowNormal = 1,

            /// <summary>
            /// 激活窗口并将其最小化
            /// </summary>
            SwShowMinimized = 2,

            /// <summary>
            /// 最大化指定的窗口
            /// </summary>
            SwMaximize = 3,
            SwShowMaximized = 3,

            /// <summary>
            /// 以窗口最近一次的大小和状态显示窗口。
            /// 激活窗口仍然维持激活状态。
            /// </summary>
            SwShowNoActivate = 4,

            /// <summary>
            /// 在窗口原来的位置以原来的尺寸激活和显示窗口
            /// </summary>
            SwShow = 5,

            /// <summary>
            /// 最小化指定的窗口并且激活在Z序中的下一个顶层窗口
            /// </summary>
            SwMinimize = 6,

            /// <summary>
            /// 窗口最小化，激活窗口仍然维持激活状态
            /// </summary>
            SwShowMinNoActive = 7,

            /// <summary>
            /// 以窗口原来的状态显示窗口。激活窗口仍然维持激活状态
            /// </summary>
            SwShowNa = 8,

            /// <summary>
            /// 激活并显示窗口。如果窗口最小化或最大化，则系统将窗口恢复到原来的尺寸和位置。
            /// 在恢复最小化窗口时，应用程序应该指定这个标志
            /// </summary>
            SwRestore = 9,

            /// <summary>
            /// 依据在STARTUPINFO结构中指定的SW_FLAG标志设定显示状态，
            /// STARTUPINFO 结构是由启动应用程序的程序传递给CreateProcess函数的
            /// </summary>
            SwShowDefault = 10,
        }
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern int ShowWindow(IntPtr hwnd, ShowWindowCmd nCmdShow);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetWindowRect(IntPtr hWnd, ref Rect lpRect);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetClientRect(IntPtr hWnd, ref Rect lpRect);
        /// <summary>
        /// 窗口与要获得句柄的窗口之间的关系。
        /// </summary>
        enum GetWindowCmd : uint
        {
            /// <summary>
            /// 返回的句柄标识了在Z序最高端的相同类型的窗口。
            /// 如果指定窗口是最高端窗口，则该句柄标识了在Z序最高端的最高端窗口；
            /// 如果指定窗口是顶层窗口，则该句柄标识了在z序最高端的顶层窗口：
            /// 如果指定窗口是子窗口，则句柄标识了在Z序最高端的同属窗口。
            /// </summary>
            GW_HWNDFIRST = 0,
            /// <summary>
            /// 返回的句柄标识了在z序最低端的相同类型的窗口。
            /// 如果指定窗口是最高端窗口，则该柄标识了在z序最低端的最高端窗口：
            /// 如果指定窗口是顶层窗口，则该句柄标识了在z序最低端的顶层窗口；
            /// 如果指定窗口是子窗口，则句柄标识了在Z序最低端的同属窗口。
            /// </summary>
            GW_HWNDLAST = 1,
            /// <summary>
            /// 返回的句柄标识了在Z序中指定窗口下的相同类型的窗口。
            /// 如果指定窗口是最高端窗口，则该句柄标识了在指定窗口下的最高端窗口：
            /// 如果指定窗口是顶层窗口，则该句柄标识了在指定窗口下的顶层窗口；
            /// 如果指定窗口是子窗口，则句柄标识了在指定窗口下的同属窗口。
            /// </summary>
            GW_HWNDNEXT = 2,
            /// <summary>
            /// 返回的句柄标识了在Z序中指定窗口上的相同类型的窗口。
            /// 如果指定窗口是最高端窗口，则该句柄标识了在指定窗口上的最高端窗口；
            /// 如果指定窗口是顶层窗口，则该句柄标识了在指定窗口上的顶层窗口；
            /// 如果指定窗口是子窗口，则句柄标识了在指定窗口上的同属窗口。
            /// </summary>
            GW_HWNDPREV = 3,
            /// <summary>
            /// 返回的句柄标识了指定窗口的所有者窗口（如果存在）。
            /// GW_OWNER与GW_CHILD不是相对的参数，没有父窗口的含义，如果想得到父窗口请使用GetParent()。
            /// 例如：例如有时对话框的控件的GW_OWNER，是不存在的。
            /// </summary>
            GW_OWNER = 4,
            /// <summary>
            /// 如果指定窗口是父窗口，则获得的是在Tab序顶端的子窗口的句柄，否则为NULL。
            /// 函数仅检查指定父窗口的子窗口，不检查继承窗口。
            /// </summary>
            GW_CHILD = 5,
            /// <summary>
            /// （WindowsNT 5.0）返回的句柄标识了属于指定窗口的处于使能状态弹出式窗口（检索使用第一个由GW_HWNDNEXT 查找到的满足前述条件的窗口）；
            /// 如果无使能窗口，则获得的句柄与指定窗口相同。
            /// </summary>
            GW_ENABLEDPOPUP = 6
        }

        /*GetWindowCmd指定结果窗口与源窗口的关系，它们建立在下述常数基础上：
              GW_CHILD
              寻找源窗口的第一个子窗口
              GW_HWNDFIRST
              为一个源子窗口寻找第一个兄弟（同级）窗口，或寻找第一个顶级窗口
              GW_HWNDLAST
              为一个源子窗口寻找最后一个兄弟（同级）窗口，或寻找最后一个顶级窗口
              GW_HWNDNEXT
              为源窗口寻找下一个兄弟窗口
              GW_HWNDPREV
              为源窗口寻找前一个兄弟窗口
              GW_OWNER
              寻找窗口的所有者
         */
        /// <summary>
        /// 该函数返回与指定窗口有特定关系（如Z序或所有者）的窗口句柄。
        /// 函数原型：HWND GetWindow（HWND hWnd，UNIT nCmd）；
        /// </summary>
        /// <param name="hWnd">窗口句柄。要获得的窗口句柄是依据nCmd参数值相对于这个窗口的句柄。</param>
        /// <param name="uCmd">说明指定窗口与要获得句柄的窗口之间的关系。该参数值参考GetWindowCmd枚举。</param>
        /// <returns>返回值：如果函数成功，返回值为窗口句柄；如果与指定窗口有特定关系的窗口不存在，则返回值为NULL。
        /// 若想获得更多错误信息，请调用GetLastError函数。
        /// 备注：在循环体中调用函数EnumChildWindow比调用GetWindow函数可靠。调用GetWindow函数实现该任务的应用程序可能会陷入死循环或退回一个已被销毁的窗口句柄。
        /// 速查：Windows NT：3.1以上版本；Windows：95以上版本；Windows CE：1.0以上版本；头文件：winuser.h；库文件：user32.lib。
        /// </returns>
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr GetWindow(IntPtr hWnd, GetWindowCmd uCmd);

        //根据坐标获取窗口句柄 
        [DllImport("user32", EntryPoint = "WindowFromPoint")]
        public static extern IntPtr WindowFromPoint(int xPoint, int yPoint);

        [DllImport("user32.dll")]
        public static extern int GetWindowTextLength(IntPtr hWnd);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int nMaxCount);

        [DllImport("user32.dll")]
        public static extern bool AddClipboardFormatListener(IntPtr hwnd);

        [DllImport("user32.dll")]
        public static extern bool RemoveClipboardFormatListener(IntPtr hwnd);

        public static int WM_CLIPBOARDUPDATE = 0x031D;

        /// <summary>
        /// 获得当前进程，以便重绘控件
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int InvalidateRect(IntPtr hWnd, IntPtr rect, int bErase);

        /// <summary>
        /// 窗口是否最小化
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern bool IsIconic(IntPtr hWnd);

        /// <summary>
        /// 使窗口前端显示
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
        /// <summary>
        /// 窗口是否还存在
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool IsWindow(IntPtr hWnd);

        /// <summary>
        /// 通过程序标题查找句柄
        /// </summary>
        /// <param name="lpClassName">窗口类型</param>
        /// <param name="lpWindowName">窗口名称</param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);


        // P/Invoke declarations
        [DllImport("gdi32.dll")]
        public static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int
        wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
        [DllImport("gdi32.dll")]
        public static extern IntPtr DeleteDC(IntPtr hDc);
        [DllImport("gdi32.dll")]
        public static extern IntPtr DeleteObject(IntPtr hDc);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
        [DllImport("user32.dll")]
        public static extern IntPtr GetDesktopWindow();

        [StructLayout(LayoutKind.Sequential)]
        public struct CURSORINFO
        {
            public int cbSize;
            public int flags;
            public IntPtr hCursor;
            public Point ptScreenPos;
        }

        [DllImport("user32.dll")]
        public static extern bool GetCursorInfo(out CURSORINFO pci);

        public const int CURSOR_SHOWING = 0x00000001;


        public static void Highlight(IntPtr hwnd)
        {
            var idRect = GetRect(hwnd.ToInt32());
            var rectangle = GetFormRegion(idRect);
            var hDC = GetWindowDC(hwnd);
            if (hDC.ToInt32() == 0)
            {
                return;
            }
            var pen = new Pen(Color.DarkRed, 5);
            var g = Graphics.FromHdc(hDC);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.DrawRectangle(pen, rectangle);
            pen.Dispose();
            //释放
            ReleaseDC(hwnd, hDC);
        }

        public static void ClearHighlight(IntPtr hwnd)
        {
            InvalidateRect(hwnd, IntPtr.Zero, 1);
        }

        private static Rectangle GetFormRegion(IdRect rect)
        {
            //double Xratio = (double)Screen.PrimaryScreen.Bounds.Width / 65535.0;
            //double Yratio = (double)Screen.PrimaryScreen.Bounds.Height / 65535.0;
            var tRect = new Rectangle(
                0, 0,
                rect.Rect.Right - rect.Rect.Left,
                rect.Rect.Bottom - rect.Rect.Top
                );
            return tRect;
        }

        /// <summary>
        ///     获取进程列表
        /// </summary>
        /// <returns>进程列表</returns>
        public static Process[] GetProcesses()
        {
            var processes = Process.GetProcesses();
            _dicProcesses = processes.ToDictionary(p => p.Id);
            return processes;
        }

        private const int SM_CYCAPTION = 4;
        private const int SM_CXBORDER = 5;
        private const int SM_CXDLGFRAME = 7;
        [DllImport("user32.dll")]
        private static extern int GetSystemMetrics(int nIndex);
        /// <summary>
        /// 窗口是否最大化
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns>true为最大化</returns>
        [DllImport("user32.dll")]
        private static extern bool IsZoomed(IntPtr hWnd);

        public static IdRect GetRectByPtr(IntPtr ptr)
        {
            var rectClient = new Rect();
            var tRectWindow = new Rect();
            GetClientRect(ptr, ref rectClient);
            GetWindowRect(ptr, ref tRectWindow);

            var rectangeWindow = tRectWindow.ToRectangle();
            var rectangeClient = rectClient.ToRectangle();
            int boundwidth = (rectangeWindow.Width - rectangeClient.Width) / 2;
            int titleHeight = (rectangeWindow.Height - rectangeClient.Height) - 2 * boundwidth;


            tRectWindow.Left += boundwidth;
            tRectWindow.Right -= boundwidth;
            tRectWindow.Top += titleHeight + boundwidth;
            tRectWindow.Bottom -= boundwidth;
            
            return new IdRect
            {
                Rect = tRectWindow,
                Ptr = ptr.ToInt32()
            };
        }

        /// <summary>
        ///     获取窗口外包矩形
        /// </summary>
        /// <param name="ptr">进程句柄</param>
        /// <returns>窗口矩形</returns>
        public static IdRect GetRect(int ptr)
        {
            IntPtr parentIntPtr = new IntPtr(ptr);
            return GetRectByPtr(parentIntPtr);

            // 以下代码只能获取到窗口内部的控件，没法获取到弹窗
            //IntPtr ptrChild = GetWindow(parentIntPtr, GetWindowCmd.GW_CHILD);        //得到屏幕上第一个子窗口
            //var tempRect = new Rect();

            //while (ptrChild != IntPtr.Zero)                    //循环得到所有的子窗口
            //{

            //    var suc = GetWindowRect(ptrChild, ref tempRect);
            //    if (suc)
            //    {
            //        Console.WriteLine(tempRect.ToRectangle());
            //        tRect.Union(tempRect);
            //    }
            //    ptrChild = GetWindow(ptrChild, GetWindowCmd.GW_HWNDNEXT);

        }



        public static RectList GetRects()
        {
            var tRectlist = new RectList();
            var count = _dicProcesses.Count;
            tRectlist.Rects = new IdRect[count];
            var index = 0;
            foreach (var dicProcess in _dicProcesses)
            {
                try
                {
                    tRectlist.Rects[index] = GetRect(dicProcess.Key);
                }
                catch (Exception)
                {
                    tRectlist.Rects[index] = new IdRect();
                    // ignored
                }
                index++;
            }
            for (var i = 0; i < count; i++)
            {

            }
            return tRectlist;
        }

        /// <summary>
        ///     获取进程列表信息
        /// </summary>
        /// <returns>进程列表信息</returns>
        public static ProccessInfoList GetProces()
        {
            var processes = GetProcesses();
            var tRects = new ProccessInfoList
            {
                ProccessInfos = new ProccessInfo[processes.Length]
            };
            var index = 0;
            foreach (var process in processes)
            {
                var tRect = new ProccessInfo
                {
                    Name = process.ProcessName,
                    Ptr = process.Handle.ToInt32()
                };
                tRects.ProccessInfos[index++] = tRect;
            }
            return tRects;
        }
    }
}